// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// package buildcfg -- go2cs converted at 2022 March 13 05:43:21 UTC
// import "internal/buildcfg" ==> using buildcfg = go.@internal.buildcfg_package
// Original source: C:\Program Files\Go\src\internal\buildcfg\exp.go
namespace go.@internal;

using fmt = fmt_package;
using reflect = reflect_package;
using strings = strings_package;

using goexperiment = @internal.goexperiment_package;


// Experiment contains the toolchain experiments enabled for the
// current build.
//
// (This is not necessarily the set of experiments the compiler itself
// was built with.)
//
// experimentBaseline specifies the experiment flags that are enabled by
// default in the current toolchain. This is, in effect, the "control"
// configuration and any variation from this is an experiment.

using System;
public static partial class buildcfg_package {



public static readonly var DefaultGOEXPERIMENT = defaultGOEXPERIMENT;

// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
//
// This used to be an experiment, but now it's always enabled on
// platforms that support it.
//
// Note: must agree with runtime.framepointer_enabled.


// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
//
// This used to be an experiment, but now it's always enabled on
// platforms that support it.
//
// Note: must agree with runtime.framepointer_enabled.
public static var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64";

// ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT)
// configuration tuple and returns the enabled and baseline experiment
// flag sets.
//
// TODO(mdempsky): Move to internal/goexperiment.
public static (goexperiment.Flags, goexperiment.Flags, error) ParseGOEXPERIMENT(@string goos, @string goarch, @string goexp) {
    goexperiment.Flags flags = default;
    goexperiment.Flags baseline = default;
    error err = default!;

    var regabiSupported = goarch == "amd64" && (goos == "android" || goos == "linux" || goos == "darwin" || goos == "windows");

    baseline = new goexperiment.Flags(RegabiWrappers:regabiSupported,RegabiG:regabiSupported,RegabiReflect:regabiSupported,RegabiDefer:regabiSupported,RegabiArgs:regabiSupported,); 

    // Start with the statically enabled set of experiments.
    flags = baseline; 

    // Pick up any changes to the baseline configuration from the
    // GOEXPERIMENT environment. This can be set at make.bash time
    // and overridden at build time.
    if (goexp != "") { 
        // Create a map of known experiment names.
        var names = make_map<@string, Action<bool>>();
        var rv = reflect.ValueOf(_addr_flags).Elem();
        var rt = rv.Type();
        for (nint i = 0; i < rt.NumField(); i++) {
            var field = rv.Field(i);
            names[strings.ToLower(rt.Field(i).Name)] = field.SetBool;
        } 

        // "regabi" is an alias for all working regabi
        // subexperiments, and not an experiment itself. Doing
        // this as an alias make both "regabi" and "noregabi"
        // do the right thing.
        names["regabi"] = v => {
            flags.RegabiWrappers = v;
            flags.RegabiG = v;
            flags.RegabiReflect = v;
            flags.RegabiDefer = v;
            flags.RegabiArgs = v;
        }; 

        // Parse names.
        foreach (var (_, f) in strings.Split(goexp, ",")) {
            if (f == "") {
                continue;
            }
            if (f == "none") { 
                // GOEXPERIMENT=none disables all experiment flags.
                // This is used by cmd/dist, which doesn't know how
                // to build with any experiment flags.
                flags = new goexperiment.Flags();
                continue;
            }
            var val = true;
            if (strings.HasPrefix(f, "no")) {
                (f, val) = (f[(int)2..], false);
            }
            var (set, ok) = names[f];
            if (!ok) {
                err = fmt.Errorf("unknown GOEXPERIMENT %s", f);
                return ;
            }
            set(val);
        }
    }
    if (goarch != "amd64") {
        flags.RegabiWrappers = false;
        flags.RegabiG = false;
        flags.RegabiReflect = false;
        flags.RegabiDefer = false;
        flags.RegabiArgs = false;
    }
    if (flags.RegabiG && !flags.RegabiWrappers) {
        err = fmt.Errorf("GOEXPERIMENT regabig requires regabiwrappers");
    }
    if (flags.RegabiArgs && !(flags.RegabiWrappers && flags.RegabiG && flags.RegabiReflect && flags.RegabiDefer)) {
        err = fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers,regabig,regabireflect,regabidefer");
    }
    return ;
}

// expList returns the list of lower-cased experiment names for
// experiments that differ from base. base may be nil to indicate no
// experiments. If all is true, then include all experiment flags,
// regardless of base.
private static slice<@string> expList(ptr<goexperiment.Flags> _addr_exp, ptr<goexperiment.Flags> _addr_@base, bool all) {
    ref goexperiment.Flags exp = ref _addr_exp.val;
    ref goexperiment.Flags @base = ref _addr_@base.val;

    slice<@string> list = default;
    var rv = reflect.ValueOf(exp).Elem();
    reflect.Value rBase = default;
    if (base != null) {
        rBase = reflect.ValueOf(base).Elem();
    }
    var rt = rv.Type();
    for (nint i = 0; i < rt.NumField(); i++) {
        var name = strings.ToLower(rt.Field(i).Name);
        var val = rv.Field(i).Bool();
        var baseVal = false;
        if (base != null) {
            baseVal = rBase.Field(i).Bool();
        }
        if (all || val != baseVal) {
            if (val) {
                list = append(list, name);
            }
            else
 {
                list = append(list, "no" + name);
            }
        }
    }
    return list;
}

// GOEXPERIMENT is a comma-separated list of enabled or disabled
// experiments that differ from the baseline experiment configuration.
// GOEXPERIMENT is exactly what a user would set on the command line
// to get the set of enabled experiments.
public static @string GOEXPERIMENT() {
    return strings.Join(expList(_addr_Experiment, _addr_experimentBaseline, false), ",");
}

// EnabledExperiments returns a list of enabled experiments, as
// lower-cased experiment names.
public static slice<@string> EnabledExperiments() {
    return expList(_addr_Experiment, _addr_null, false);
}

// AllExperiments returns a list of all experiment settings.
// Disabled experiments appear in the list prefixed by "no".
public static slice<@string> AllExperiments() {
    return expList(_addr_Experiment, _addr_null, true);
}

// UpdateExperiments updates the Experiment global based on a new GOARCH value.
// This is only required for cmd/go, which can change GOARCH after
// program startup due to use of "go env -w".
public static void UpdateExperiments(@string goos, @string goarch, @string goexperiment) {
    error err = default!;
    Experiment, experimentBaseline, err = ParseGOEXPERIMENT(goos, goarch, goexperiment);
    if (err != null) {
        Error = err;
    }
}

} // end buildcfg_package
